/**
 * Copyright © 2018 深圳市巨鼎医疗设备有限公司
 */
package com.gxitsky.utils;

import com.gxitsky.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author gxing
 * @desc 日期工具类
 * @date 2021-12-20
 * @see org.apache.commons.lang3.time.DateFormatUtils
 * @see org.apache.commons.lang3.time.DateUtils
 * @since V1.0.0
 */
public class DateUtil extends DateUtils {

    /**
     * yyyy
     */
    public static final String PATTERN_YYYY_NORMAL = "yyyy";
    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final String PATTERN_YMDHMS_NORMAL = "yyyy-MM-dd HH:mm:ss";

    /**
     * yyyyMMddHHmmss
     */
    public static final String PATTERN_YMDHMS_CONNECT = "yyyyMMddHHmmss";

    /**
     * yyyy-MM-dd
     */
    public static final String PATTERN_YMD_NORMAL = "yyyy-MM-dd";

    /**
     * yyyyMMdd
     */
    public static final String PATTERN_YMD_CONNECT = "yyyyMMdd";

    /**
     * yyyy-MM-dd HH:mm
     */
    public static final String PATTERN_YMDHM_NORMAL = "yyyy-MM-dd HH:mm";

    /**
     * yyyyMMddHHmm
     */
    public static final String PATTERN_YMDHM_CONNECT = "yyyyMMddHHmm";

    /**
     * yyyyMMddHHmmssSSS
     */
    public static final String PATTERN_YMDHMSMS_CONNECT = "yyyyMMddHHmmssSSS";

    /**
     * yyyy/MM/dd HH:mm:ss
     */
    public static final String PATTERN_YMDHMS_SLASH = "yyyy/MM/dd HH:mm:ss";

    /**
     * yyyy/MM/dd
     */
    public static final String PATTERN_YMDH_SLASH = "yyyy/MM/dd";

    /**
     * yyyy/MM/dd HH:mm
     */
    public static final String PATTERN_YMDHM_SLASH = "yyyy/MM/dd HH:mm";

    public static String[] DEFAULT_PATTERNS = {DateUtil.PATTERN_YMD_CONNECT, DateUtil.PATTERN_YMDHMS_NORMAL,
            DateUtil.PATTERN_YMD_NORMAL, DateUtil.PATTERN_YMDHMS_CONNECT, DateUtil.PATTERN_YMD_CONNECT,
            DateUtil.PATTERN_YMDH_SLASH, DateUtil.PATTERN_YMDHMS_SLASH};

    /**
     * 各种格式字符串转换成日期
     *
     * @param str 字符串日期时间
     * @return Date
     */
    public static Date parseDate(final String str) {
        return parseDate(str, DEFAULT_PATTERNS);
    }

    /**
     * 各种格式字符串转换成日期
     *
     * @param str           字符串日期时间
     * @param parsePatterns 日期时间格式
     * @return Date
     */
    public static Date parseDate(final String str, final String... parsePatterns) {
        try {
            return parseDate(str, null, parsePatterns);
        } catch (ParseException e) {
            throw new BusinessException("日期【" + str + "】格式不正确", e);
        }
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern.
     * </p>
     *
     * @param millis  the date to format expressed in milliseconds
     * @param pattern the pattern to use to format the date, not null
     * @return the formatted date
     */
    public static String format(final long millis, final String pattern) {
        return DateFormatUtils.format(new Date(millis), pattern, null, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern.
     * </p>
     *
     * @param date    the date to format, not null
     * @param pattern the pattern to use to format the date, not null
     * @return the formatted date
     */
    public static String format(final Date date, final String pattern) {
        return DateFormatUtils.format(date, pattern, null, null);
    }

    /**
     * <p>
     * Formats a calendar into a specific pattern.
     * </p>
     *
     * @param calendar the calendar to format, not null
     * @param pattern  the pattern to use to format the calendar, not null
     * @return the formatted calendar
     * @see FastDateFormat#format(Calendar)
     * @since 2.4
     */
    public static String format(final Calendar calendar, final String pattern) {
        return DateFormatUtils.format(calendar, pattern, null, null);
    }

    /**
     * 获取一年的第几周
     *
     * @param dateStr
     * @return
     * @throws ParseException
     */
    public static int weekOfYear(String dateStr) throws ParseException {
        return weekOfYear(parseDate(dateStr, PATTERN_YMDHMS_NORMAL, PATTERN_YMDHMS_CONNECT, PATTERN_YMD_NORMAL,
                PATTERN_YMD_CONNECT));
    }

    /**
     * 获取一年的第几周
     *
     * @param date
     * @return
     */
    public static int weekOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * 获取一周的第几天：周日为第一天
     *
     * @param dateStr
     * @return
     * @throws ParseException
     */
    public static int dayOfWeek(String dateStr) throws ParseException {
        return dayOfWeek(parseDate(dateStr, PATTERN_YMDHMS_NORMAL, PATTERN_YMDHMS_CONNECT, PATTERN_YMD_NORMAL,
                PATTERN_YMD_CONNECT));
    }

    /**
     * 获取一周的第几天：周日为第一天
     *
     * @param date
     * @return
     */
    public static int dayOfWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * 获取月份
     *
     * @param date
     * @return
     */
    public static int monthOfYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取年份
     *
     * @param date
     * @return
     */
    public static int year(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.YEAR);
    }

    /**
     * 获取当月第几天
     *
     * @param date
     * @return
     */
    public static int dayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 根据生日计算年龄
     *
     * @param birthday
     * @return
     * @throws ParseException
     */
    public static int calculateAge(String birthday) {
        Date parseDate = parseDate(birthday, PATTERN_YMDHMS_NORMAL, PATTERN_YMDHMS_CONNECT, PATTERN_YMD_NORMAL,
                PATTERN_YMD_CONNECT);
        int dateMillis = 1000 * 60 * 60 * 24;
        return (int) ((System.currentTimeMillis() / dateMillis - parseDate.getTime() / dateMillis) / 365F);
    }

    /**
     * 计算当前日期的前后天数
     *
     * @return
     * @author 杜浩
     * @date 2020/7/20 13:54
     */
    public static Date getDateBeforeOrAfter(Date curDate, int day, boolean isBefore) {
        Calendar now = Calendar.getInstance();
        now.setTime(curDate);
        if (isBefore) {
            now.set(Calendar.DATE, now.get(Calendar.DATE) - day);
        } else {
            now.set(Calendar.DATE, now.get(Calendar.DATE) + day);
        }
        return now.getTime();
    }

    /**
     * 获取几天后的日期
     *
     * @param day
     * @return
     */
    public static Date getAfterDay(int day) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, day);
        return calendar.getTime();
    }

    /**
     * 获取今天开始时间
     *
     * @return
     */
    public static Date getBeginDatetimeOfToday() {
        return getBeginDatetime(Calendar.getInstance());
    }

    /**
     * 获取N天后的开始时间
     *
     * @param amount
     * @return
     */
    public static Date getBeginDatetimeAfterSomedays(int amount) {
        final Calendar c = Calendar.getInstance();
        c.add(Calendar.DAY_OF_MONTH, amount);
        return getBeginDatetime(c);
    }

    /**
     * 获取date的开始时间
     *
     * @param date
     * @return
     */
    public static Date getBeginDatetime(Date date) {
        final Calendar c = Calendar.getInstance();
        c.setTime(date);
        return getBeginDatetime(c);
    }

    /**
     * 获取c的开始时间
     *
     * @param c
     * @return
     */
    public static Date getBeginDatetime(Calendar c) {
        c.setLenient(false);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 获取今天结束时间
     *
     * @return
     */
    public static Date getEndDatetimeOfToday() {
        return getEndDatetime(Calendar.getInstance());
    }

    /**
     * 获取N天后的结束时间
     *
     * @param amount
     * @return
     */
    public static Date getEndDatetimeAfterSomedays(int amount) {
        final Calendar c = Calendar.getInstance();
        c.add(Calendar.DAY_OF_MONTH, amount);
        return getEndDatetime(c);
    }

    /**
     * 获取date的结束时间
     *
     * @param date
     * @return
     */
    public static Date getEndDatetime(Date date) {
        final Calendar c = Calendar.getInstance();
        c.setTime(date);
        return getEndDatetime(c);
    }

    /**
     * 获取c的结束时间
     *
     * @param c
     * @return
     */
    public static Date getEndDatetime(Calendar c) {
        c.setLenient(false);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime();
    }

    /**
     * 计算两个时间中间间隔天数
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int daysBetween(Date date1, Date date2) {
        long differenceMillis = date2.getTime() - date1.getTime();
        long oneDateMillis = TimeUnit.DAYS.toMillis(1);
        return (int) (differenceMillis % oneDateMillis == 0 ? Math.abs(differenceMillis / oneDateMillis)
                : (Math.abs(differenceMillis / oneDateMillis) + 1));
    }

    /**
     * 当前时间增加
     *
     * @param calendarField
     * @param amount
     * @return
     */
    public static Calendar addFromNow(final int calendarField, final int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(calendarField, amount);
        return calendar;
    }

    /**
     * 获取指定日期具体时间点
     *
     * @param date
     * @param hour
     * @param minute
     * @param second
     * @param millis
     * @return
     */
    public static Date getDateTime(Date date, int hour, int minute, int second, int millis) {
        validateDateNotNull(date);
        final Calendar c = Calendar.getInstance();
        c.setLenient(false);
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, hour);
        c.set(Calendar.MINUTE, minute);
        c.set(Calendar.SECOND, second);
        c.set(Calendar.MILLISECOND, millis);
        return c.getTime();
    }

    /**
     * 校验时间不为空
     *
     * @param date
     */
    public static void validateDateNotNull(final Date date) {
        Validate.isTrue(date != null, "The date must not be null");
    }

    /**
     * 获取今天
     */
    public static String getToday() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern(PATTERN_YMD_NORMAL));
    }

    /**
     * 获取昨天
     */
    public static String getYesterday() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -1);
        Date yesterday = calendar.getTime();
        return DateFormatUtils.format(yesterday, PATTERN_YMD_NORMAL);
    }

    /**
     * 获取本周的第一天
     *
     * @return String
     **/
    public static String getWeekStart() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.WEEK_OF_MONTH, 0);
        calendar.set(Calendar.DAY_OF_WEEK, 2);
        Date weekStart = calendar.getTime();
        return DateFormatUtils.format(weekStart, PATTERN_YMD_NORMAL);
    }

    /**
     * 获取本周的最后一天
     *
     * @return String
     **/
    public static String getWeekEnd() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_WEEK, calendar.getActualMaximum(Calendar.DAY_OF_WEEK));
        calendar.add(Calendar.DAY_OF_WEEK, 1);
        Date weekEnd = calendar.getTime();
        return DateFormatUtils.format(weekEnd, PATTERN_YMD_NORMAL);
    }

    /**
     * 获取本月的第一天
     *
     * @return
     */
    public static String getMonthStart() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        Date firstDay = calendar.getTime();
        return DateFormatUtils.format(firstDay, PATTERN_YMD_NORMAL);
    }

    /**
     * 获取本月的最后一天
     *
     * @return
     */
    public static String getMonthEnd() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date lastDay = calendar.getTime();
        return DateFormatUtils.format(lastDay, PATTERN_YMD_NORMAL);
    }

    /**
     * 获取本年的第一天
     *
     * @return String
     **/
    public static String getYearStart() {
//        String yyyy = LocalDate.now().format(DateTimeFormatter.ofPattern(PATTERN_YYYY_NORMAL));
//        return yyyy + "-01-01";
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MONTH, calendar.getActualMinimum(Calendar.MONTH));
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
        Date yearStartDay = calendar.getTime();
        return DateFormatUtils.format(yearStartDay, PATTERN_YMD_NORMAL);
    }

    /**
     * 获取本年的最后一天
     *
     * @return String
     **/
    public static String getYearEnd() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MONTH, calendar.getActualMaximum(Calendar.MONTH));
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date yearLastDay = calendar.getTime();
        return DateFormatUtils.format(yearLastDay, PATTERN_YMD_NORMAL);
    }

    /**
     * @param date1 日期1
     * @param date2 日期2
     * @desc 两个日期相差天数
     * @author gxing
     * @date 2021/12/23 18:12
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        int days = (int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24));
        return days;
    }

    /**
     * @param startDateStr 开始时间
     * @param endDateStr   结束时间
     * @desc 获取时间范围内的所有日期
     * @author gxing
     * @date 2021/12/23 18:55
     */
    public static TreeSet<String> getDays(String startDateStr, String endDateStr) {
        if (StringUtils.isBlank(startDateStr) || StringUtils.isBlank(endDateStr)) {
            throw new BusinessException("开始时间或结束时间不能为空");
        }
        Date startDate = DateUtil.parseDate(startDateStr, DateUtil.PATTERN_YMD_NORMAL);
        Date endDate = DateUtil.parseDate(endDateStr, DateUtil.PATTERN_YMD_NORMAL);

        String nextDayStr = startDateStr;
        Date nextDay = startDate;
        TreeSet<String> treeSet = new TreeSet<>();
        while (true) {
            boolean after = nextDay.after(endDate);
            if (!after) {
                treeSet.add(nextDayStr);
                nextDay = DateUtil.addDays(nextDay, 1);
                nextDayStr = DateUtil.format(nextDay, DateUtil.PATTERN_YMD_NORMAL);
            } else {
                break;
            }
        }
        return treeSet;
    }

//    public static void main(String[] args) {
//        TreeSet<String> days = getDays("2021-10-01", "2021-10-31");
//        System.out.println(JSON.toJSONString(days));
//    }
}
